﻿#ifndef MAC_UTILS_HPP
#define MAC_UTILS_HPP

#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <iomanip>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <cstdint>
#include <iostream>

#ifdef _WIN32
#include <Winsock2.h>
#include <Iphlpapi.h>
#pragma comment(lib,"Iphlpapi")
#pragma warning( disable : 4996 )
#define MACSESION 6
#else
#include <string.h>
#include <algorithm>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#endif // !_WIN32

namespace mv {
class mac_utils {
public:
#ifdef _WIN32
	static bool get_mac(std::string& mac) {
		PIP_ADAPTER_ADDRESSES pAddresses = NULL;
		ULONG family = AF_INET;
		ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
		ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES);

		// Make an initial call to GetAdaptersAddresses to get the
		// size needed into the outBufLen variable
		if (GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) {
			pAddresses = static_cast<PIP_ADAPTER_ADDRESSES>(HeapAlloc(GetProcessHeap(), 0, outBufLen));
		}
		_ASSERT(pAddresses);
		// Make a second call to GetAdapters Addresses to get the
		// actual data we want
		DWORD dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
		if (dwRetVal != ERROR_SUCCESS) {
			return false;
		}
		bool found = false;
		PIP_ADAPTER_ADDRESSES pFirst = pAddresses;
		while (pAddresses) {
			BYTE* pa = pAddresses->PhysicalAddress;
			if (!pa || !pa[0]) {
				pAddresses = pAddresses->Next ? pAddresses->Next : NULL;
				continue;
			}
			char bAddressBytes[MACSESION];
			int bAddressInt[MACSESION];
			memset(bAddressBytes, 0, MACSESION);
			size_t nAddressSize = pAddresses->PhysicalAddressLength;
			memcpy(bAddressBytes, pa, (nAddressSize < MACSESION ? nAddressSize : MACSESION));
			char addr[MACSESION * 3] = { 0 };
			for (int i = 0; i < MACSESION; ++i) {
				bAddressInt[i] = bAddressBytes[i];
				bAddressInt[i] &= 0x000000FF; // avoid "ff" leading bytes when "char" is lager then 0x7f
			}
			sprintf(addr, "%02X%02X%02X%02X%02X%02X",
				bAddressInt[0],
				bAddressInt[1],
				bAddressInt[2],
				bAddressInt[3],
				bAddressInt[4],
				bAddressInt[5]); // Should use scl::FormatString inside
			pAddresses = pAddresses->Next ? pAddresses->Next : NULL;
			mac = std::string(addr);
			found = true;
			break;
		}
		HeapFree(GetProcessHeap(), 0, pFirst);
		return found;
	}
#else
	static bool get_mac(std::string& mac) {
		std::string script = "cat /proc/net/dev | awk '{i++; if(i>2){print $1}}' | sed 's/^[\t]*//g' | sed 's/[:]*$//g'";
		FILE* pf = popen(script.c_str(), "r");
		char line[50] = { 0 };
		std::vector<std::string> vecs;
		while (fgets(line, 50, pf)) {
			int len = strlen(line);
			if (line[len - 1] == '\n') {
				line[len - 1] = 0;
			}
			if (line == std::string("lo")) {
				continue;
			}
			vecs.push_back(line);
		}
		pclose(pf);

		if (vecs.size() < 1) {
			return false;
		}
		std::sort(vecs.begin(), vecs.end());
		std::string best;
		std::string heads[] = { "eth", "en" };
		for (auto& head : heads) {
            if (__best_match(vecs, head, best)) {
                break;
            }
		}
        if (best.empty()) {
            best = vecs[0];
        }
		struct ifreq ifreq = { 0 };
		int sock = 0;
		char ms[32] = "";
		sock = socket(AF_INET, SOCK_STREAM, 0);
		if (sock < 0) {
			return false;
		}
		strcpy(ifreq.ifr_name, best.c_str());
		if (ioctl(sock, SIOCGIFHWADDR, &ifreq) < 0) {
			return false;
		}

		for (int i = 0; i < 6; i++) {
			sprintf(ms + 2 * i, "%02X", (unsigned char)ifreq.ifr_hwaddr.sa_data[i]);
		}
		ms[strlen(ms)] = 0;
		mac = std::string(ms);

		return true;
	}

	static bool __best_match(const std::vector<std::string>& names, const std::string& head, std::string& name) {
		std::vector<std::string> __vec;
		for (auto& item : names) {
			if (item.compare(0, head.size(), head) != 0) {
				continue;
			}
			__vec.push_back(item);
		}
		if (__vec.empty()) {
			return false;
		}
		name = __vec[0];

		return true;
	}
#endif // !_WIN32
private:
	static void get_mac_bytes(const std::string& mac, std::vector<uint8_t>& bytes) {
		const std::string hexChars("0123456789ABCDEF");
		bytes = std::vector<uint8_t>(mac.length() / 2);
		for (int i = 0; i < bytes.size(); i++) {
			char ch = mac[i * 2];
			uint8_t bh = (uint8_t)hexChars.find_first_of(ch, 0);
			ch = mac[i * 2 + 1];
			uint8_t bl = (uint8_t)hexChars.find_first_of(ch, 0);
			bytes[i] = (uint8_t)((bh << 4) | bl);
		}
	}
};
} // namespace mv

#endif // !MAC_UTILS_HPP
